/*
-----------------------------------------------------------------------------
This source file is part of SapphireEngine

SapphireEngine is a Framework for Turn Based Strategy games.
For more information see http://code.google.com/p/sapphire-engine/

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.

-----------------------------------------------------------------------------
*/
#ifndef _UNIT_H
#define _UNIT_H

#include "Core/SapphireConfig.h"

namespace SapphireEngine
{
	namespace Core
	{
		#define DISTANCE_TOLERANCE 5

		enum UnitState
		{
			IDLE,
			MOVE,
			ATTACK,
			KILLED
		};

		class Unit
		{
		public:
			Unit(Part *basePart=0,Ogre::SceneManager *sceneMgr = Ogre::Root::getSingletonPtr()->getSceneManager("Default SceneManager"))
				:mSpeed(getCoreManager()->getTerrainTypes()),
				mArmor(getCoreManager()->getDamageTypes()),
				mRotating(true),
				mRotProgress(0),
				//mFormation(0),
				mSceneMgr(sceneMgr)
			{
				if(basePart)
					mBodyParts.push_back(static_cast<Body *>(basePart->clone(mSceneMgr)));
			}

			bool addPart(Part *newPart, Part *exsistingPart=0, Ogre::SceneNode *node=0)
			{
				Part * part = newPart->clone(mSceneMgr);
				if(exsistingPart)
					exsistingPart->addChild(part,node);
				else
				{
					delete mBodyParts.front();
					mBodyParts.insert(mBodyParts.begin(),static_cast<Body *>(part));
				}
				updateStats();
				return true;		
			}
			void updateStats()
			{
				Body *basePart = mBodyParts.front();

				mBodyParts.clear();
				mMovementParts.clear();
				mWeaponParts.clear();
				
				mSpeed.clearVals();
				mArmor.clearVals();
				mHitPoints=0;
				
				_updateStatsRec(basePart);
			}
			
			bool onFrameUpdate(const Ogre::FrameEvent &evt)
			{
				switch(mState)
				{	
				case MOVE:
					//for(std::vector<Movement *>::iterator itr=mMovementParts.begin();itr!=mMovementParts.end();itr++)
					//{
					//	(*itr)->getAnimationState()->addTime(evt.timeSinceLastFrame);
					//}
					//const ubyte speed = mPathPoints.front().second * evt.timeSinceLastFrame;  
					//if (mRotating)
					//	_rotateStep(speed);
					//_moveStep(speed);//TODO: add funcionality for accelerate and decelerate;
					//if (mBodyParts.front()->getNode()->getPosition().positionCloses(mPathPoints.front().first,DISTANCE_TOLERANCE))
					//{
					//	mPathPoints.pop_front();
					//	mRotating=true;
					//}
					//if (mPathPoints.empty())
					//	setState(IDLE);
					break;
				case ATTACK:
					//for(std::vector<Ammo *>::iterator itr=mAmmoParts.begin();itr!=mAmmoParts.end();itr++)
					//{
					//	Ogre::Vector3 pos = (*itr)->advance();//TODO
					//	if (pos.positionCloses(mTarget,DISTANCE_TOLERANCE))
					//	{
					//		(*itr)->hit();//TODO
					//		mAmmoParts.erase(itr);
					//	}
					//}
					//if (mAmmoParts.empty)
					//	setState(IDLE);
					break;
				case KILLED:
					break;
				default:
				case IDLE:
					/*for(std::vector<Movement *>::iterator itr=mMovementParts.begin();itr!=mMovementParts.end();itr++)
					{
						(*itr)->getAnimationState()->addTime(evt.timeSinceLastFrame);
					}*/
					break;
				}
				return true;
			}

			void setState(const UnitState &state)
			{
				/*mState=state;
				switch(mState)
				{	
				case MOVE:
					for(std::vector<Movement *>::iterator itr=mMovementParts.begin();itr!=mMovementParts.end();itr++)
					{
						(*itr)->setAnimationState("Move");
						(*itr)->getAnimationState()->setLoop(true);
						(*itr)->getAnimationState()->setEnabled(true);
					}
					break;
				case ATTACK:
					break;
				case KILLED:
					break;
				default:
				case IDLE:
					for(std::vector<Movement *>::iterator itr=mMovementParts.begin();itr!=mMovementParts.end();itr++)
					{
						(*itr)->setAnimationState("Idle");
						(*itr)->getAnimationState()->setLoop(true);
						(*itr)->getAnimationState()->setEnabled(true);
					}
					break;
				}*/
			}
			Unit *clone()
			{}//TODO

			//Formation *getFormation(){return mFormation;}
			//void setFormation(Formation *formation){mFormation = formation;}

		private:
			void _rotateStep(const ubyte &speed)
			{
				//Ogre::Vector3 direction = mPathPoints.front().first - mBodyParts.front()->getNode()->getPosition();     
				//Ogre::Quaternion src = mBodyParts.front()->getNode()->getOrientation();// * Ogre::Vector3::UNIT_X;      
				//src.y = 0;                                                    
				//direction.y = 0;                                             
				//src.normalise();                                              
				//direction.normalise();  
				//Ogre::Quaternion quat = src.getRotationTo(direction);
				////mBodyParts.front()->getNode()->rotate(quat);
				//
				//Ogre::Real rotFactor = 1.0f /speed;
				//Ogre::Quaternion orientIntA;
				//Ogre::Quaternion orientIntB;
				//
				//Ogre::Vector3 dest = quat * src;           // We want dest orientation, not a relative rotation (quat)

				//orientIntA = Ogre::Quaternion::Slerp(.3, src, dest, true);
				//orientIntB = Ogre::Quaternion::Slerp(.5, src, dest, true);

				//mRotProgress += rotFactor;
				//if(mRotProgress>1)
				//{
				//	mRotating = false;
				//	mRotProgress = 0;
				//}
				//else
				//{
				//	Ogre::Quaternion delta = Ogre::Quaternion::Squad(mRotProgress, src, orientIntA, orientIntB, dest, true);
				//	mBodyParts.front()->getNode()->setOrientation(delta);
				//}
			}
			void _moveStep(const ubyte &speed)
			{
				Ogre::Vector3 dest = mBodyParts.front()->getNode()->getPosition();
				dest.z+=speed;
				mBodyParts.front()->getNode()->setPosition(dest);
			}
			void _updateStatsRec(Part *part)
			{
				if (part->getPartType().compare("Body"))
					mBodyParts.push_back(static_cast<Body *>(part));
				else if (part->getPartType().compare("Movement"))
					mMovementParts.push_back(static_cast<Movement *>(part));
				else if (part->getPartType().compare("Weapon"))
					mWeaponParts.push_back(static_cast<Weapon *>(part));

				mSpeed+= part->getSpeed();
				mArmor+= part->getArmor();
				mHitPoints+= part->getHitPoints();

				std::vector<Part *> *childParts = part->getChildParts();

				for(std::vector<Part *>::const_iterator itr=childParts->begin(); itr!=childParts->end();itr++)
					_updateStatsRec(*itr);
			}

			//Formation *mFormation;
			std::vector<Body *> mBodyParts;
			std::vector<Movement *> mMovementParts;
			std::vector<Weapon *> mWeaponParts;

			GameFactor mSpeed;
			GameFactor mArmor;
			long mHitPoints;

			UnitState mState;

			std::deque<std::pair<Ogre::Vector3,ubyte>> mPathPoints;
			Ogre::Real mRotProgress;
			bool mRotating;

			Ogre::Vector3 mTarget;

			Ogre::SceneManager *mSceneMgr;
		};
	}
}

#endif